package cc.wanforme.fsync.server;

import cc.wanforme.fsync.config.restUpload.IDFileSystemResource;
import cc.wanforme.fsync.config.restUpload.UploadProgressCenter;
import cc.wanforme.fsync.entity.*;
import cc.wanforme.fsync.entity.device.DeviceInfo;
import cc.wanforme.fsync.entity.device.DeviceInfoVo;
import cc.wanforme.fsync.entity.device.HeartBeat;
import cc.wanforme.fsync.entity.po.ClientNode;
import cc.wanforme.fsync.entity.po.GroupMap;
import cc.wanforme.fsync.entity.po.SyncLog;
import cc.wanforme.fsync.server.service.IClientNodeService;
import cc.wanforme.fsync.server.service.IGroupMapService;
import cc.wanforme.fsync.server.service.ISyncLogService;
import cc.wanforme.fsync.server.task.SyncEvent;
import cc.wanforme.fsync.util.DateTimeTool;
import cc.wanforme.st.server.base.constant.ResCode;
import cc.wanforme.st.server.exception.MunkRuntimeException;
import cc.wanforme.st.server.util.AssertCheck;
import cc.wanforme.st.server.util.HttpTool;
import cc.wanforme.fsync.util.LoggerTool;
import cc.wanforme.st.server.vo.ResMessage;
import com.fasterxml.jackson.core.type.TypeReference;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.File;
import java.nio.file.AccessDeniedException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

@Service
public class ServerCenter {
    private static final Logger log = LoggerTool.getLogger();
    @Value("${server.servlet.context-path:}")
    private String baseContext;
    // 所有的设备
    private ConcurrentLinkedDeque<ClientNodeInfo> clients = new ConcurrentLinkedDeque<>();

    private HttpTool httpTool;
    private Environment environment;


//    // 同步文件的配置
//    private SyncConfig config;

    private IClientNodeService clientNodeService;
    private IGroupMapService groupMapService;
    private ISyncLogService syncLogService;
    private ApplicationEventPublisher eventPublisher;

    // 启动完成之后查询所有的客户端端点，客户端端点以内存中的为主
    public void initClients() {
        List<ClientNode> all = clientNodeService.list();
        for (ClientNode item : all) {
            // 获取 node 的信息
            ClientNodeInfo info = new ClientNodeInfo(item);
            try {
                boolean su = refreshClient(info);
                info.setOnline(su);
            } catch (Exception e) {
                info.setOnline(false);
                log.error("connect fail！ " + info.getHost() + ":" + info.getPort(), e);
            }
            clients.add(info);
        }
    }

    private ClientNodeInfo getInfo(ClientNode node) {
        Optional<ClientNodeInfo> any = clients.stream().filter(e -> node.customEquals(e)).findAny();
        return any.orElse(null);
    }

    public ClientNodeInfo getInfoById(Long id) {
        Optional<ClientNodeInfo> any = clients.stream().filter(e -> e.getId().longValue() == id.longValue()).findAny();
        return any.orElse(null);
    }

    /**
     * 刷新客户端的信息，成功后，则添加客户端
     */
    public synchronized ClientNodeInfo connectClient(ClientNode node) {
        ClientNodeInfo oldInfo = getInfo(node);
        ClientNodeInfo info = oldInfo != null ? oldInfo : new ClientNodeInfo(node);

        boolean su = false;
        try {
            su = refreshClient(info); // 获取 node 的信息
            info.setOnline(su);
        } catch (Exception e) {
            log.error("fail to connect Client " + node.getHost() + ":" + node.getPort(), e);
        }

        if (!su) {
            throw new MunkRuntimeException("fail to connect Client " + node.getHost() + ":" + node.getPort());
        }

        if (oldInfo == null) {
            // 添加到数据库
            if (node.getId() == null) {
                clientNodeService.saveVo(node);
            } else {
                clientNodeService.updateVo(node);
            }

            info.setId(node.getId());
            clients.add(info);
            return info;
        } else {
            throw new MunkRuntimeException("The client has been added");
        }

    }

    /**
     * (http://127.0.0.1:8052, /client/beat) => http://127.0.0.1:8052/jfs/client/beat
     */
    private String clientUrl(String origin, String subUrl) {
        String context = baseContext == null || baseContext.equals("/") ? "" : baseContext;
        return origin + context + subUrl;
    }

    /**
     * 请求获取客户端的信息，添加客户端的磁盘信息
     */
    public boolean refreshClient(ClientNodeInfo node) {
        String connectUrl = clientUrl(node.base(), "/client/beat");// node.base() + "/jfs/client/beat";
        // 连接成功，则添加到列表中
        ResMessage<HeartBeat> res = httpTool.post(connectUrl, null,
                new ParameterizedTypeReference<ResMessage<HeartBeat>>() {
                });
        if (res.getCode().equals(ResCode.SUCCESS.getCode())) {
            if (res.getData() != null) {
                List<DeviceInfo> list = res.getData().getDeviceInfos();
                if (list != null) {
                    List<DeviceInfoVo> vos = list.stream().map(DeviceInfoVo::new).collect(Collectors.toList());
                    node.setDeviceInfo(vos);
                }
            }
            return true;
        }
        return false;
    }

//    /**
//     * 更新同步文件的配置
//     */
//    public void updateSyncConfig(SyncConfig config) {
//        this.config = config;
//    }

    // ====== 同步文件 start========

    /**
     * 自动区分同步文件夹还是文件
     *
     * @param meta 同步参数
     * @param time 同步的时间
     */
    public ResMessage<String> async(SyncMeta meta, LocalDateTime time) throws Exception {
        // meta.target 有重写的情况，检查重写规则。
        if (!StringUtils.hasText(meta.getRewritePath())) {
            meta.setRewritePath(null);
        } else {
            meta.setRewritePath(meta.getRewritePath().trim());
        }

        String error = SyncMetaDto.checkRewritePath(meta.getRewritePath());
        if (error != null) {
            return ResMessage.fail(error);
        }

        Callable<ResMessage<String>> task = () -> sync(meta, time);
        FutureTask<ResMessage<String>> futureTask = new FutureTask<>(task);
        try {
            (new Thread(futureTask)).start();
            return futureTask.get(5, TimeUnit.SECONDS); // 等待5秒
        } catch (TimeoutException e) {
            return ResMessage.success("已在后台开启任务");
        }
    }

    /**
     * 自动区分同步文件夹还是文件。线程同步
     *
     * @param meta 同步参数
     * @param time 同步的时间
     */
    public ResMessage<String> sync(SyncMeta meta, LocalDateTime time) throws Exception {
        String source = meta.getSource();
        File file = new File(source);
        if (file.exists()) {
            ResMessage.fail("file not existed");
        }

        // server 本地复制
        if(isSameNode(meta)) {
            SyncMetaDto dto = SyncMetaDto.from(meta, meta.getSource(), meta.getTarget());
            dto.setSource("");
            dto.setTarget("");
            return syncLocalFile(dto, time);
        }

        if (file.isDirectory()) {
            // 设置 dto ，重置 source 和 target。 source 和 target均为子路径
            SyncMetaDto dto = SyncMetaDto.from(meta, meta.getSource(), meta.getTarget());
            dto.setSource("");
            dto.setTarget("");
            return syncFolder(dto, time);
        } else {
            SyncMetaDto dto = SyncMetaDto.from(meta, null, null);
            //dto.setIgnoreUnchanged(true);  // （废弃方案-原因：复杂） 处理时单文件，忽略未变化的文件
            return syncFile(dto, time);
        }
    }

    /**
     * 同步文件夹。线程同步
     *
     * @param meta 同步参数
     * @param time 同步的时间
     */
    public ResMessage<String> syncFolder(SyncMetaDto meta, LocalDateTime time) throws Exception {
        String source = meta.getSource();
        String target = meta.getTarget();

//        File folder = new File(source);
        File folder = new File(meta.getSourceBase(), source);
        File[] fs = folder.listFiles();
        if (fs == null || fs.length == 0) {
            return ResMessage.success("Empty folder");
        }

        // 对比 server 与 client 的文件，筛选需要更新的文件，以及需要删除 client 多余的文件
        //String targetPath = new File(meta.getTargetBase(), target).getPath();
        String targetPath = meta.decodeRewritePath(time); // 还有动态生成的那部分路径
        ResMessage<List<FileInfoVo>> clientInfoRes = listClientFiles(meta.getClientNode(), targetPath);
        FilteredFiles filteredFiles = filterFiles(
                FileInfoVo.listFileVos(folder.getPath()),
                clientInfoRes.getData());

        // 更新
        List<FileInfoVo> updates = filteredFiles.getUpdates();
        if (updates != null && !updates.isEmpty()) {
            // 处理需要 同步的文件
//            for(File f : fs) {
            for (FileInfoVo f : updates) {
                String sFile = source + "/" + f.getName();
                String tFile = target + "/" + f.getName();

//            SyncMeta subMeta = SyncMeta.from(meta);
//            subMeta.setSource(sFile);
//            subMeta.setTarget(tFile);
                SyncMetaDto subMeta = SyncMetaDto.from(meta, meta.getSourceBase(), meta.getTargetBase());
                subMeta.setSource(sFile);
                subMeta.setTarget(tFile);
                // 文件夹已经过滤了未变化的文件，同步文件时不需要再次检测是否发生变化
                subMeta.setIgnoreUnchanged(false);

                File tsf = new File(meta.getSourceBase(), sFile);
                if (tsf.isDirectory()) {
                    syncFolder(subMeta, time);
                } else {
                    syncFile(subMeta, time);
                }
            }
        }

        // 删除
        if (meta.getFileOnlyOnClient() == 1) {
            List<FileInfoVo> deletes = filteredFiles.getDeletes();
            if (deletes != null && !deletes.isEmpty()) {
                for (FileInfoVo f : deletes) {
                    SyncMetaDto subMeta = SyncMetaDto.from(meta, meta.getSourceBase(), meta.getTargetBase());
                    deleteClientFile(meta.getClientNode(), subMeta, f.getPath(), time);
                }
            }
        }

        return ResMessage.success("");
    }

    /**
     * 同步单个文件，并记录日志。线程同步
     *
     * @param meta 同步参数
     * @param time 同步的时间
     */
    public ResMessage<String> syncFile(SyncMetaDto meta, LocalDateTime time) throws Exception {
        ResMessage<String> res = null;
        SyncLog syncLog = SyncLog.from(meta); // 记录
        syncLog.setCreateTime(DateTimeTool.format(time));

        try {
            syncLogService.save(syncLog);
            // 发布同步事件
            eventPublisher.publishEvent(new SyncEvent(SyncEvent.BEFORE, syncLog));

            // 对 server 和 client 是同一个时的优化。全都走else中的逻辑也可以。
            res = innerSyncFile(meta, time, syncLog.getId());
        } catch (AccessDeniedException e) {
            syncLog.setErrorMsg("Access Denied:" + e.getMessage());
            log.error("Access Denied:" + e.getMessage(), e);
        } catch (Exception e) {
            syncLog.setErrorMsg(e.getMessage());
            log.error("", e);
        } finally {
            if (res != null) {
                syncLog.setStatus(res.getCode());
                syncLog.setResult(res.getinfo());
            }
            syncLog.setUpdateTime(DateTimeTool.format(LocalDateTime.now()));
            //syncLogService.save(syncLog);
            syncLogService.updateById(syncLog);
            // 发布同步事件
            eventPublisher.publishEvent(new SyncEvent(SyncEvent.FINISH, syncLog));
        }

        return res;
    }

    /**
     * 同步单个文件，并记录日志。线程同步
     *
     * @param meta 同步参数
     * @param time 同步的时间
     */
    public ResMessage<String> syncLocalFile(SyncMetaDto meta, LocalDateTime time) throws Exception {
        ResMessage<String> res = null;
        SyncLog syncLog = SyncLog.from(meta); // 记录
        syncLog.setCreateTime(DateTimeTool.format(time));

        try {
            syncLogService.save(syncLog);
            // 发布同步事件
            eventPublisher.publishEvent(new SyncEvent(SyncEvent.BEFORE, syncLog));

            res = syncCopyFile(meta, time, syncLog.getId());
        } catch (AccessDeniedException e) {
            syncLog.setErrorMsg("Access Denied:" + e.getMessage());
            log.error("Access Denied:" + e.getMessage(), e);
        } catch (Exception e) {
            syncLog.setErrorMsg(e.getMessage());
            log.error("", e);
        } finally {
            if (res != null) {
                syncLog.setStatus(res.getCode());
                syncLog.setResult(res.getinfo());
            }
            syncLog.setUpdateTime(DateTimeTool.format(LocalDateTime.now()));
            syncLogService.updateById(syncLog);
            // 发布同步事件
            eventPublisher.publishEvent(new SyncEvent(SyncEvent.FINISH, syncLog));
        }

        return res;
    }


    /**
     * 同步单个文件
     * meta
     */
    public ResMessage<String> innerSyncFile(SyncMetaDto meta, LocalDateTime time, Long syncLogId) throws Exception {
        ClientNode node = meta.getClientNode();

        String addUri = clientUrl(node.base(), "/client/add");//node.base() + "/client/add";
        String updateUri = clientUrl(node.base(), "/client/update");// node.base() + "/client/update";

        if (!StringUtils.hasText(meta.getSource()) || !StringUtils.hasText(meta.getTarget())) {
            return ResMessage.fail("Invalid path");
        }

        // 先检查是否能连通，
        ClientNodeInfo info = getInfo(node);
        //AssertCheck.nonNull(info, "Client has not been connected"); // 检查是否已经连接此 Client，防止胡乱请求
        if (info == null) {
            return ResMessage.fail("Client has not been connected");
        }
        try {
            refreshClient(info);
        } catch (Exception e) {
            log.error("Can't connect client while synchronizing " + node.getHost() + ":" + node.getPort(), e);
            return ResMessage.fail("Can't connect client");
        }

        String path = meta.decodeRewritePath(time);
        String url = meta.isCover() ? updateUri : addUri;
        String sourceFile = meta.getSourceBase() + meta.getSource();
//        FileSystemResource resource = new FileSystemResource(sourceFile);
        Resource resource = new IDFileSystemResource(syncLogId, sourceFile);

        // 跳过未变化的文件
        if (meta.isIgnoreUnchanged()) {
            ResMessage<FileInfoVo> infoRes = getClientFileInfo(node, path);
            if (ResCode.SUCCESS.getCode().equals(infoRes.getCode())) {
                FileInfoVo data = infoRes.getData(); // data 为null则说明不存在
                if (data != null && data.getModifyTime() == resource.lastModified()) {
                    return ResMessage.success("unchanged, skip", null);
                }
            }
            // 失败表示文件不存在，继续后续逻辑
        }

        // 上传文件
//        ResMessage<String> res = httpTool.uploadFile(url, path, resource,
//                new TypeReference<ResMessage<String>>() {}
//        );
        try {
            ResMessage<String> res = httpTool.uploadFileWithProgress(url, path, resource,
                    new TypeReference<ResMessage<String>>() {}
            );
            return res;
        } finally {
            // 删掉进度
            UploadProgressCenter.closeTracker(syncLogId);
        }

    }
    public ResMessage<String> syncCopyFile(SyncMetaDto meta, LocalDateTime time, Long syncLogId) throws Exception {
        ClientNode node = meta.getClientNode();

        String path = meta.decodeRewritePath(time);
        String uri = clientUrl(node.base(), "/client/copyLocal");
        String sourceFile = meta.getSourceBase() + meta.getSource();

//        Map<String, String> params = new HashMap<>(2);
//        params.put("source", HttpTool.encParam(sourceFile)); // 源文件
//        params.put("target", HttpTool.encParam(path)); // 目标文件的路径
        //System.out.println("source: " + sourceFile +"\ntarget:" + path);
        CopyLocalVo params = new CopyLocalVo( sourceFile, path);

        try {
            ResMessage<String> res = httpTool.post(uri, params,
                    new ParameterizedTypeReference<ResMessage<String>>() {
                    });
            return res;
        } finally {
            // 删掉进度
            UploadProgressCenter.closeTracker(syncLogId);
        }

    }


    /**
     * 获取 client 的文件信息
     */
    public ResMessage<FileInfoVo> getClientFileInfo(ClientNode node, String path) {
        String uri = clientUrl(node.base(), "/client/info");
        Map<String, String> param = new HashMap<>(1);
        param.put("path", path);

        ResMessage<FileInfoVo> res = httpTool.post(uri, param,
                new ParameterizedTypeReference<ResMessage<FileInfoVo>>() {
                });
        return res;
    }

    // ====== 同步文件 end========

    /**
     * 列出 client 的文件
     */
    public ResMessage<List<FileInfoVo>> listClientFiles(ClientNode node, String path) {
        String uri = clientUrl(node.base(), "/client/list"); //node.base() + "/client/list";
        Map<String, String> param = new HashMap<>(1);
        param.put("path", path);

        ResMessage<List<FileInfoVo>> res = httpTool.post(uri, param,
                new ParameterizedTypeReference<ResMessage<List<FileInfoVo>>>() {
                });

        return res;
    }

    // ====== client操作 start========

    /**
     * 删除一个节点
     */
    public void remove(ClientNode node) {
        ClientNodeInfo info = getInfo(node);
        if (info != null) {
            clientNodeService.removeById(info.getId());
            clients.remove(info);
        }
    }

    public ConcurrentLinkedDeque<ClientNodeInfo> getClients() {
        return clients;
    }

    /**
     * 更新 ClientNode (根据id更新)
     */
    public void updateClientById(ClientNode client) {
        AssertCheck.nonNull(client.getId(), "unknown client");
        ClientNodeInfo oldInfo = getInfoById(client.getId());

        // 连接失败则抛出异常，那么更新失败
        ClientNodeInfo newInfo = new ClientNodeInfo(client);
        boolean su = refreshClient(newInfo); // 获取 node 的信息

        try {
            if (su) {
                // 如果成功，更新数据库
                clientNodeService.updateVo(client);
                // 移除旧信息
                if (oldInfo != null) {
                    clients.remove(oldInfo);
                }
                clients.add(newInfo);
            }
        } catch (Exception e) {
            //clients.remove(newInfo);
            throw e;
        }
    }

    public void deleteClientById(Long id) {
        ClientNodeInfo info = getInfoById(id);
        if (info != null) {
            clients.remove(info);
        }
        clientNodeService.removeById(info);

        // TODO 关联的 group 需要删除

    }
    // ====== client操作 end========

    /**
     * group 所有的 groupMap
     */
    public List<GroupMapVo> listGroupMaps(String name) {
        List<GroupMap> list = groupMapService.listByGroups(name);
        if (list == null) {
            return new ArrayList<>(0);
        }

        List<GroupMapVo> vos = new ArrayList<>(list.size());
        for (GroupMap gm : list) {
            Long sourceId = gm.getSourceClient();
            Long targetId = gm.getTargetClient();

            GroupMapVo vo = new GroupMapVo(gm);
            if (sourceId != null) {
                vo.setSourceClientNode(clientNodeService.getById(sourceId));
                //TODO detect space
            }
            if (targetId != null) {
                vo.setTargetClientNode(clientNodeService.getById(targetId));
                //TODO detect space
            }
            vos.add(vo);
        }

        return vos;
    }

    /**
     * 删除 client 的文件
     */
    public void deleteClientFile(ClientNode node, SyncMetaDto meta, String path, LocalDateTime time) {
        // todo record test
        String uri = clientUrl(node.base(), "/client/del"); //node.base() + "/client/list";
        Map<String, String> param = new HashMap<>(1);
        param.put("path", path);

        ResMessage<Boolean> res = null;
        SyncLog syncLog = SyncLog.from(meta); // 记录
        syncLog.setTarget(path); // client 上需要删除的文件
        syncLog.setCreateTime(DateTimeTool.format(time));
        try {
            res = httpTool.post(uri, param,
                    new ParameterizedTypeReference<ResMessage<Boolean>>() {
                    });
//        } catch (AccessDeniedException e) {
//            syncLog.setErrorMsg("Access Denied:" + e.getMessage());
//            log.error("Access Denied:" + e.getMessage(), e);
        } catch (Exception e) {
            syncLog.setErrorMsg(e.getMessage());
            log.error("", e);
        } finally {
            if (res != null) {
                syncLog.setStatus(res.getCode());
                if (res.getData() != null && res.getData().booleanValue()) {
                    syncLog.setResult("deleted");
                } else {
                    syncLog.setResult(res.getinfo());
                }
            }
            syncLog.setUpdateTime(DateTimeTool.format(LocalDateTime.now()));
            syncLogService.save(syncLog);
        }


    }


    //>>>>>>>>>>>>同步文件夹-筛选>>>>>>>>>>>>>>

    /**
     * 过滤出 需要更新和删除的文件，<br/>
     * server 端中的文件夹直接归为需要更新
     */
    private FilteredFiles filterFiles(List<FileInfoVo> servers, List<FileInfoVo> clients) {
        List<FileInfoVo> adds = new ArrayList<>();
        List<FileInfoVo> updates = new ArrayList<>();
        List<FileInfoVo> deletes = new ArrayList<>();

        if (clients == null) {
            clients = new ArrayList<>(0);
        }

        // 交集(需要更新的)
        List<FileInfoVo> copyVos = new ArrayList<>(servers);
        updates = this.retainTags(copyVos, clients);

        // 差集(新增)，server有
        adds = this.removeTags(copyVos, clients);

        // 差集(删除)，原数据库有的，现在更新没了（tags已变成需要删除的集合）
        deletes = this.removeTags(clients, copyVos);

        List<FileInfoVo> finalUpdates = new ArrayList<>(adds);
        finalUpdates.addAll(updates);

        FilteredFiles ff = new FilteredFiles();
        ff.setUpdates(finalUpdates);
        ff.setDeletes(deletes);
        return ff;
    }

    /**
     * 判断是否相同
     */
    public boolean isSame(FileInfoVo t1, FileInfoVo t2) {
        return Objects.equals(t1.getName(), t2.getName());
    }

    /**
     * 查询两个集合的交集，需要更新的
     *
     * @param source
     * @param another
     */
    public List<FileInfoVo> retainTags(List<FileInfoVo> source, List<FileInfoVo> another) {
        List<FileInfoVo> list = source.stream().filter(e -> {
            Optional<FileInfoVo> findAny = another.stream()
                    .filter(ae -> isSame(e, ae)
                            && e.getModifyTime() != ae.getModifyTime())
                    .findAny();
            return findAny.isPresent();
        }).collect(Collectors.toList());
        return list;
    }

    /**
     * 从 source 中删除 another中含有的元素
     *
     * @param source
     * @param another 需要删除的部分
     */
    public List<FileInfoVo> removeTags(List<FileInfoVo> source, List<FileInfoVo> another) {
        List<FileInfoVo> list = source.stream().filter(e -> {
            Optional<FileInfoVo> findAny = another.stream()
                    .filter(ae -> isSame(e, ae)).findAny();
            return !findAny.isPresent();
        }).collect(Collectors.toList());
        ;
        return list;
    }
    //>>>>>>>>>>>>同步文件夹-筛选>>>>>>>>>>>>>>

    private static final List<String> localHosts = Arrays.asList("localhost", "127.0.0.1");

    /** 相同端口，且 host 是回环地址，即 localhost 和 127.0.0.1<br/> 不考虑其它IP <br/>
     * 不考虑其它 IP ，且必需同端口是为了防止本机代理的情况 */
    public boolean isSameNode(SyncMeta meta) {
        // 目前仅能处理 server 所在的机器本地复制文件
        ClientNode clientNode = meta.getClientNode();

        if(!Objects.equals(clientNode.getPort(), getPort())) {
            return false;
        }
        return localHosts.contains(clientNode.getHost());
    }

    public String getPort(){
        return environment.getProperty("local.server.port");
    }

    @Autowired
    public void setHttpTool(HttpTool httpTool) {
        this.httpTool = httpTool;
    }

    @Autowired
    public void setClientNodeService(IClientNodeService clientNodeService) {
        this.clientNodeService = clientNodeService;
    }

    @Autowired
    public void setGroupMapService(IGroupMapService groupMapService) {
        this.groupMapService = groupMapService;
    }

    @Autowired
    public void setSyncLogService(ISyncLogService syncLogService) {
        this.syncLogService = syncLogService;
    }
    @Autowired
    public void setEventPublisher(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }
    @Autowired
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
}
